﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using HeartLess.Core;
using HeartLess.Managers;
using HeartLess.Core.Input;
using HeartLess.Components;
using HeartLess.Activities;

namespace HeartLess.GameObjects
{
    public class Player : CollidableGameObject
    {
        private Animation _animation;
        private bool _isJumping;
        private bool _isShooting;

        private Point _initialLocation;
        private Single _initialHealth;
        private Single _initialHeartRate;

        private IWeapon _weapon;

        private Single _health;
        private Single _heartRate;
        private Single _startHealth;
        private Single _startTime;
        private Single _lastDelta;
        private bool _tracking;
        private List<Modifier> _modifierList = new List<Modifier>();

        private bool _filter = false; // testing filter

        private readonly Vector2 RUN_VELOCITY = new Vector2(250, 0);
        private readonly Vector2 JUMP_VELOCITY = new Vector2(0, -800);

        private enum Animations
        {
            IDLE,
            RUN_LEFT,
            RUN_RIGHT,
            JUMP,
            SHOCKWAVE
        }

        private Animations _currentAnimation;
        private Animations CurrentAnimation
        {
            set
            {
                if (value == _currentAnimation) return;
                
                switch (value)
                {
                    case Animations.IDLE:
                        StartIdleAnimation();
                        break;

                    case Animations.JUMP:
                        bool movingLeft = (_currentAnimation == Animations.RUN_LEFT);
                        StartJumpingAnimation(movingLeft);
                        break;

                    case Animations.RUN_LEFT:
                        StartRunningAnimation(true);
                        break;

                    case Animations.RUN_RIGHT:
                        StartRunningAnimation(false);
                        break;

                    case Animations.SHOCKWAVE:
                        StartShockWaveAnimation();
                        break;

                }

                _currentAnimation = value;
            }
        }

        public Player(Point spawnPoint, Single health, Single heartRate)
            : base(spawnPoint)
        {
            _initialHealth = health;
            _initialHeartRate = heartRate;
            _initialLocation = spawnPoint;
            GravityEnabled = true;
            StartPlayer();
            addCollisionTrigger(this);

            _weapon = new ShockWaveGun();
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            IInput input = InputManager.Input;

            if (_isShooting == false)
            {
                bool movingLeft = input.IsMovingLeft;
                bool movingRight = input.IsMovingRight;
                if ((movingLeft ^ movingRight))
                {
                    if (movingLeft)
                    {
                        MoveLeft();
                    }
                    else
                    {
                        MoveRight();
                    }
                }
                else if (_isJumping == false)
                {
                    CurrentAnimation = Animations.IDLE;
                }

                if (input.IsPressingJump && IsAirbourne == false)
                {
                    Jump();
                }

                _weapon.Update(gameTime);
                if (input.IsPressingFire)
                {
                    FireWeapon();
                }
            }

            // Loop through the modifiers and let them expire
            for (int i = 0; i < _modifierList.Count(); ++i)
            {
                Modifier mod = _modifierList[i];
                if (mod.update(gameTime, this))
                {
                    _modifierList.RemoveAt(i);
                    --i;
                }
            }

            _animation.Update(gameTime);

            Activities.GameActivity game = ActivityManager.CurrentActivity as Activities.GameActivity;
            if (game != null)
                UpdateHealth(game.Hud, gameTime);
        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            _animation.Draw(spriteBatch);

            _weapon.Draw(spriteBatch);
        }

        public void AddModifier(Modifier modifier)
        {
            _modifierList.Add(modifier);
        }

        public void AddHealth(Single delta)
        {
            _startHealth += delta;
        }

        public Single HeartRate
        {
            get
            {
                return _heartRate;
            }
            set
            {
                _heartRate = value;
                _startHealth = _health + _lastDelta * _heartRate / 60.0f;
            }
        }

        public void StartPlayer()
        {
            Location = HeartLess.LevelData.Level.getLocationFromGridPoint(_initialLocation);
            _tracking = false;
            StartIdleAnimation();
        }

        public void PlayerDies()
        {
            GameActivity.Instance.RestartLevel();
        }

        protected override void onMove()
        {
            _animation.Location = this.Location;
            Screen.FocusCameraOn(this.Location);
            if (_animation.Location.Y > Screen.WINDOW_HEIGHT)
                PlayerDies();
        }

        protected override void handleCollision(CollidableGameObject collidedObject)
        {
            //do nothing
        }

        private void UpdateHealth(Core.Hud hud, GameTime gameTime)
        {
            if (!_tracking)
            {
                _startTime = (Single)gameTime.TotalGameTime.TotalMilliseconds / 1000.0f;
                _startHealth = _initialHealth;
                _heartRate = _initialHeartRate;
                _tracking = true;
            }

            _lastDelta = (Single)gameTime.TotalGameTime.TotalMilliseconds / 1000.0f - _startTime;
            _health = _startHealth - _heartRate * _lastDelta / 60.0f;
            hud.Health = _health;
            hud.HeartRate = _heartRate;
            if (_health <= 0)
                PlayerDies();
        }

        #region Input Actions

        private void MoveRight()
        {
            bool moved = tryMove(RUN_VELOCITY);

            if (_isJumping == false)
            {
                CurrentAnimation = (moved) ? Animations.RUN_RIGHT : Animations.IDLE;
            }
        }

        private void MoveLeft()
        {
            bool moved = tryMove(-RUN_VELOCITY);

            if (_isJumping == false)
            {
                CurrentAnimation = (moved) ? Animations.RUN_LEFT : Animations.IDLE;
            }
        }

        private void Jump()
        {
            bool jumped = tryMove(JUMP_VELOCITY);
            if (jumped)
            {
                CurrentAnimation = Animations.JUMP;
                _isJumping = true;
            }
        }

        private void FireWeapon()
        {
            _weapon.Fire(this.Location);


            HeartRateModifier.ChangeHeartRateFactor(this, 3f, 0.5f);

            _isShooting = true;
            CurrentAnimation = Animations.SHOCKWAVE;
        }
        
        #endregion

        #region Animations

        private void StartIdleAnimation()
        {
            Texture2D idleAnimationTexture = AssetManager.GetAnimationTexture(AssetManager.ANIMATIONS.PLAYER_IDLE);
            _animation = new Animation(idleAnimationTexture, 32, 60);
            _animation.Location = this.Location;
        }

        private void StartRunningAnimation(bool isRunningLeft)
        {
            Texture2D runningAnimationTexture = AssetManager.GetAnimationTexture(AssetManager.ANIMATIONS.PLAYER_RUNNING);
            _animation = new Animation(runningAnimationTexture, 36, 14);
            _animation.IsFlippedHorizonally = isRunningLeft;
            _animation.Location = this.Location;
        }

        private void StartJumpingAnimation(bool isJumpingLeft)
        {
            Texture2D jumpingAnimationTexture = AssetManager.GetAnimationTexture(AssetManager.ANIMATIONS.PLAYER_JUMPING);
            _animation = new Animation(jumpingAnimationTexture, 16, 6);
            _animation.RepeatEnabled = false;
            _animation.Location = this.Location;
            _animation.IsFlippedHorizonally = isJumpingLeft;
            _animation.OnAnimationCompleted = JumpAnimationCompleted;
        }

        private void JumpAnimationCompleted()
        {
            _isJumping = false;
        }

        private void StartShockWaveAnimation()
        {
            Texture2D idleAnimationTexture = AssetManager.GetAnimationTexture(AssetManager.ANIMATIONS.PLAYER_SHOCKWAVE);
            _animation = new Animation(idleAnimationTexture, 10, 25);
            _animation.Location = this.Location;
            _animation.RepeatEnabled = false;
            _animation.OnAnimationCompleted = ShockWaveAnimationCompleted;
        }

        private void ShockWaveAnimationCompleted()
        {
            _isShooting = false;
        }

        #endregion

        public abstract class Modifier
        {
            private TimeSpan _expires;
            private bool _started = false;

            public Modifier(TimeSpan delta)
            {
                _expires = delta;
            }

            public bool update(GameTime gameTime, Player player)
            {
                if (!_started)
                {
                    _expires += gameTime.TotalGameTime;
                    _started = true;
                }

                if (_expires < gameTime.TotalGameTime)
                {
                    OnExpiration(player);
                    return true;
                }
                return false;
            }

            public abstract void OnExpiration(Player player);
        }
    }
}
